/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab-modules.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

//===========================================================================================
#include <sstream>

//===========================================================================================
#include "OIS_Input.h"
#include <dylab/simulation/Simulation.h>
#include <dylab/utils/exceptions/EInvalidState.h>

//===========================================================================================
const dylab::String dylab::OIS_Input::COMPONENT_NAME = "ois";

//===========================================================================================
dylab::OIS_Input::OIS_Input() 
: ComInput(COMPONENT_NAME), mInputSystem(NULL), mMouse(NULL), mKeyboard(NULL)
{
}
//-----------------------------------------------------------------------------
dylab::OIS_Input::~OIS_Input() 
{
    // if the input system was initialized
    if (mInputSystem != NULL) 
    {
		// destroy mouse input
        if (mMouse) 
        {
            mInputSystem->destroyInputObject(mMouse);
            mMouse = NULL;
        }

		// destroy keyboard input
        if (mKeyboard) 
        {
            mInputSystem->destroyInputObject(mKeyboard);
            mKeyboard = NULL;
        }

		// destroy the input system itself
		OIS::InputManager::destroyInputSystem(mInputSystem);
        mInputSystem = NULL;
    }
}
//-----------------------------------------------------------------------------
dylab::String dylab::OIS_Input::getDescription() const
{
    // build ois description String
    return String::fmt("%1% %2%.%3%.%4% %5%", getFullName(), 
		OIS_VERSION_MAJOR, OIS_VERSION_MINOR, OIS_VERSION_PATCH, OIS_VERSION_NAME);
}
//-----------------------------------------------------------------------------
void dylab::OIS_Input::initialize(Simulation * _parentSimulation)
{      
    // check the pointers
    DYLAB_ASSERT(_parentSimulation != NULL);
    mParentSimulation = _parentSimulation;

    // get window handle    

    // get window handle    
    size_t windowHandle = 0;
    ComGfx::IWindow::ScopedShLock(_parentSimulation->getGfx())->getPlatformHandle(&windowHandle);
    std::ostringstream windowHandleStr;
    windowHandleStr << static_cast<unsigned int>(windowHandle);

    // fill parameter list    
    OIS::ParamList paramList;
    paramList.insert(std::make_pair(std::string("WINDOW"), windowHandleStr.str()));

    try
    {
        // create inputsystem
        mInputSystem = OIS::InputManager::createInputSystem(paramList);

        // at least one keyboard is needed
        if (mInputSystem->numKeyboards() == 0)
            throw EOperationFailed("OIS input system didn't found any keyboard device!");
    
        // create a buffered keyboard
        mKeyboard = dynamic_cast<OIS::Keyboard*>(mInputSystem->createInputObject(OIS::OISKeyboard, true));
        mKeyboard->setEventCallback(this);
    
        // at least one mouse is needed
        if (mInputSystem->numMice() == 0)
            throw EOperationFailed("OIS input system didn't found any mouse device!");

        // create a buffered mouse
        mMouse = dynamic_cast<OIS::Mouse*>(mInputSystem->createInputObject(OIS::OISMouse, true));
        mMouse->setEventCallback(this);

        // get window size
        uint32_t width, height, depth;
        int32_t  left, top;
        ComGfx::ScopedShLock(_parentSimulation->getGfx())->getWindow()->getMetrics(width, height, depth, left, top);

        // set mouse region
        setWindowExtents(width, height);
    }
    catch (OIS::Exception & e)
    {        
        throw EOperationFailed(String::fmt("OIS_SimulatioInput initialization failed:'%1%'", String(e.eText)));
    }
}
//-----------------------------------------------------------------------------
void dylab::OIS_Input::loadFromPropertyTree(const PropertyTree & _pt)
{ 
	// there are currently no properties to load
}
//-----------------------------------------------------------------------------
dylab::Simulation * dylab::OIS_Input::getParentSimulation()
{
    return mParentSimulation;
}
//-----------------------------------------------------------------------------
const dylab::Simulation * dylab::OIS_Input::getParentSimulation() const
{
    return mParentSimulation;
}
//-----------------------------------------------------------------------------
void dylab::OIS_Input::setWindowExtents(uint32_t width, uint32_t height)
{
    //
    if (mMouse == NULL)
        throw EInvalidState("Mouse is not initialized.");

    //
    const OIS::MouseState &mouseState = mMouse->getMouseState();
    mouseState.width  = width;
    mouseState.height = height;
}
//-----------------------------------------------------------------------------
void dylab::OIS_Input::capture()
{
    // capture mouse input events
    if (mMouse == NULL)
        throw EInvalidState("Mouse is not initialized.");

    mMouse->capture(); 

    // capture mouse input events
    if (mKeyboard == NULL)
        throw EInvalidState("Keyboard is not initialized.");

    mKeyboard->capture();
}
//-----------------------------------------------------------------------------
bool dylab::OIS_Input::keyPressed(const OIS::KeyEvent &e)
{
    // prepare the InputEvent data structure
    InputEvent::keycode_t keyCode = static_cast<InputEvent::keycode_t>(e.key); // direct mapping

    // push the event to the queue
    mParentSimulation->getInputEventDispatcher()->sendEvent(InputEvent(InputEvent::KEY_DOWN, keyCode));

    // OIS event was processed
    return true;
}
//-----------------------------------------------------------------------------
bool dylab::OIS_Input::keyReleased(const OIS::KeyEvent &e)
{
    // prepare the InputEvent data structure
    InputEvent::keycode_t keyCode = static_cast<InputEvent::keycode_t>(e.key); // direct mapping

    // push the event to the queue
    mParentSimulation->getInputEventDispatcher()->sendEvent(InputEvent(InputEvent::KEY_UP, keyCode));

    // OIS event was processed
    return true;
}
//-----------------------------------------------------------------------------
bool dylab::OIS_Input::mouseMoved(const OIS::MouseEvent &e)
{
    // prepare the InputEvent data structure
	uint32_t mouseXAbs = e.state.X.abs;
	uint32_t mouseYAbs = e.state.Y.abs;
	real_t mouseXRel = static_cast<real_t>(e.state.X.rel) / static_cast<real_t>(e.state.width);
    real_t mouseYRel = static_cast<real_t>(e.state.Y.rel) / static_cast<real_t>(e.state.height);
    uint32_t mouseButtons = e.state.buttons;

    // push the event to the queue
    mParentSimulation->getInputEventDispatcher()->sendEvent(
		InputEvent(InputEvent::MOUSE_MOVE, mouseXAbs, mouseYAbs, mouseXRel, mouseYRel, mouseButtons));

    // OIS event was processed
    return true;
}
//-----------------------------------------------------------------------------
bool dylab::OIS_Input::mousePressed(const OIS::MouseEvent &e, OIS::MouseButtonID id)
{
    // prepare the InputEvent data structure
    uint32_t mouseXAbs = e.state.X.abs;
    uint32_t mouseYAbs = e.state.Y.abs; 
    real_t mouseXRel = static_cast<real_t>(e.state.X.rel) / static_cast<real_t>(e.state.width);
    real_t mouseYRel = static_cast<real_t>(e.state.Y.rel) / static_cast<real_t>(e.state.height);
    uint32_t mouseButtons = e.state.buttons;

    // push the event to the queue
    mParentSimulation->getInputEventDispatcher()->sendEvent(
		InputEvent(InputEvent::MOUSE_DOWN, mouseXAbs, mouseYAbs, mouseXRel, mouseYRel, mouseButtons));

    // OIS event was processed
    return true;
}
//-----------------------------------------------------------------------------
bool dylab::OIS_Input::mouseReleased(const OIS::MouseEvent &e, OIS::MouseButtonID id)
{
    // prepare the InputEvent data structure
    uint32_t mouseXAbs = e.state.X.abs;
    uint32_t mouseYAbs = e.state.Y.abs;
    real_t mouseXRel = static_cast<real_t>(e.state.X.rel) / static_cast<real_t>(e.state.width);
    real_t mouseYRel = static_cast<real_t>(e.state.Y.rel) / static_cast<real_t>(e.state.height);
    uint32_t mouseButtons = e.state.buttons;

    // push the event to the queue
    mParentSimulation->getInputEventDispatcher()->sendEvent(
		InputEvent(InputEvent::MOUSE_UP, mouseXAbs, mouseYAbs, mouseXRel, mouseYRel, mouseButtons));

    // OIS event was processed
    return true;
}
//-----------------------------------------------------------------------------